package edu.siu.math.egut.main;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

import edu.siu.math.egut.io.Algorithms;
import edu.siu.math.egut.io.LiE;
import edu.siu.math.egut.io.OutputPref;
import edu.siu.math.egut.util.Polynomial;
import edu.siu.math.egut.util.PolynomialReadException;
import edu.siu.math.egut.util.Unipotent;
/**
 * DCA stands for Double Coset Analysis.  The set up is this.  Let G be a split connected  
 * simply connected algebraic group.  For EGUT, we assume G is simple and of one of the 
 * five exceptional types, but the theory here is general.  As always, we assume G to be equipped with 
 * a choice of split maximal torus T (used to define roots) Borel subgroup B=TN (used to define 
 * simple roots and positive roots, as well as standard parabolics) and a system of 
 * epinglage, chosen so that the structure constants match the algorithm in the paper of 
 * Gilkey and Seitz, with the numbering used in LiE (which is different from that of Gilkey-Seitz
 * in the case of G2).
 * 
 * We consider two parabolics, P=MU and Q=LV, and a psi-factor psi on U.  Let us assume that psi
 * is in fact a character of U (though no checking of this is performed).  Let S < M denote the 
 * stabilizer of psi. The purpose of this program is to help with the analysis of the (U, psi) 
 * Fourier coefficient of an Eisenstein series induced from the parabolic Q.  This Fourier 
 * coefficient breaks naturally into terms indexed by Q\G/SU.  Our goal is to analyze these 
 * terms.
 * 
 * As a first approximation, we may consider Q\G/P, which is represented by minimal-length elements
 * of the Weyl group.  Based on experience, we consider only the double coset containing the longest 
 * element of this Weyl group.  The representative for this double coset will be denoted w0, and 
 * may be computed using the LiE command  "lr_reduce(qVector2, long_word, pVector2)," where qVector2 
 * and pVector2 list the simple roots of G that lie in L and M respectively.  The command lr_reduce 
 * is not built into LiE but is defined in the manual and in the EGUT LiE initfile.
 * 
 * Let P1 = M \cap w_0^{-1} Q w_0.  It is a standard parabolic of M.  Let P_\psi =M_\psi U_\psi
 * denote the largest standard parabolic of M such that M_\psi < S.  Finally, for any element 
 * nu of W, let N_nu = N \cap nu^{-1} \overline{N} \nu, where \overline{N} denotes the maximal 
 * unipotent opposite to N.  
 * 
 * We need to analyze P1\M/S.  One may show that every element contains a representative of the 
 * form nu0 delta, where nu0 is an element of N_M(T) representing an element of W(M,T) which 
 * is of minimal length in W(M1, T)\W(M,T)/W(M_psi, T), and delta is an element on N_{nu0}.
 * 
 * A more advanced version of this program may one day compute the desired set of elements nu0 
 * from P,Q, and psi.  The present one does not, it assumes that they are stored in a LiE-formatted 
 * matrix written to a plaintext file and requires the filename. 
 * 
 * Once the relevant data is extracted from the args, the following steps are performed for each nu0.
 * 
 * (1) Display nu0.
 * (2) Compute and display the set of positive roots made negative by nu0.  (By definition, they 
 * will all be roots of T in M.)  This can be computed using the EGUT LiE initfile command "goesDown( nu0)."
 * (3) Compute  the set of roots of T in U left positive by w0nu0, and display a side-by-side comparison
 * of these roots and the roots that they are mapped to, with those mapped into L shown first and those
 * mapped into V shown second.  The set may be computed using the EGUT LiE initfile command "goesUp( w0^nu0, U)."
 * It is also image( inverse(nu0), goesUp( w0, U)).  Comparisons may be obtained using the "compare" 
 * command, after intersecting with L and V respectively.
 * (4) Compute and display the set of roots of T in U made negative by w0nu0. This may be computed using the 
 * EGUT LiE initfile commands "goesDown( w0^nu0,  U)," "image( inverse( nu0), goesDown( w0))," or 
 * complement( goesDown( w0^nu0), goesDown( nu0))," reflecting two alternate descriptions of the same set:  it 
 * is the image under nu0^{-1} of the set of positive roots made negative by w0, and it is the set of roots 
 * made negative by w0nu0, but not by nu0. 
 * (5) Compute and display a certain character (or more general psi-factor if the original psi was not a character)
 *  \psi_delta(u') of U' := U\cap (w_0nu0)^{-1} N w_0nu0 (which is the product of the one-dimensional unipotent subgroups 
 *  indexed by the set of roots computed in (3)).  This character is defined in terms of an element delta of N_nu0 
 *  (which is the product of the one-dimensional unipotent subgroups indexed by the set of roots computed in (2)).
 *  To be precise delta^{-1} u' \delta is in U and \psi_\delta(u') = \psi( delta^{-1} u' \delta).
 * 
 * @author Joseph Hundley
 *
 */
public class DCA_old {
    private static final OutputPref ERR = OutputPref.ERR;
    private static final OutputPref SHOW=OutputPref.SHOW;
    private static final OutputPref HIDE = OutputPref.HIDE;

    /**
     * @param args parabolic, parabolic, polynomial, filename
     */
    public static void main(String[] args) {
	
	if( args.length <4){
	    System.out.print("Not enough args.  Need: two vectors specifying parabolics, a polynomial psi factor," +
	    		"and the name of a text file which the desired collection of Weyl elements is stored in a LiE " +
	    		"matrix.");
	    System.exit(1);
	    
	}
	
	
	String pString =args[0];
	String qString = args[1];
	
	Polynomial psi = Polynomial.create(1);
	    try {
		psi = Polynomial.read(args[2]);
	    } catch (PolynomialReadException e1) {
		System.out.println("Problem reading psi.");
		e1.printStackTrace();
		System.exit(1);
	    }
	Polynomial psi1= psi;
	String s;
	
	try {
	    s = Algorithms.fileToString(args[3]);
	} catch (FileNotFoundException e) {
	    System.out.println("File "+args[3]+" was not found.");
	    s="[[1]],[[1]]";  // to ensure that s is always initialized before its use later... though this 
	    // line would never be read...
	    System.exit(1);
	}
	int[][] m = new int[1][1];
	try {
	    

	    m = Algorithms.readLiEMatrix(s);
	} catch (Exception e) {
	    System.out.println("Problem reading file.");
	    e.printStackTrace();
	    System.exit(1);
	}
	
	int rank = m[0].length;
	
	if( type(rank) == null){
	    System.out.println("File found, matrix retreived, rows are not of " +
	    		"a length corresponding to the rank of an exceptional group.");
	    System.exit(1);
	}

	if(!checkPara(pString, rank) || !checkPara(qString, rank) ){
	    System.out.println("Parabolics either have improper format" +
	    		" or have digits out of range.(rank detected ="+rank+")");
	    System.exit(1);
	}
	    
	String groupName = type(rank)+rank;
	try {
	    
	    LiE lie = new LiE();
	    
		// set default group   
	    lie.exec( "setdefault "+groupName, ERR);
	    
	    // get w0.
	    lie.exec("w0 = lr_reduce(complement("+qString+"),long_word, complement("+pString+"))", ERR);
	    
	    for( int i = 0 ; i < m.length; i++){
		// define  nu0 in the LiE process
		lie.exec("nu0 = W_word("+lieVectorString(m[i])+")", ERR);
		
		// display nu0
		System.out.println("nu0 = ");
		lie.exec("nu0", SHOW);

		// display goesDown( nu0), and collect it into an int[][]
		say("Positive roots sent down by nu0:");
		int[][] deltaRoots = Algorithms.readLiEMatrix(lie.exec("goesDown( nu0) ", SHOW));
		
		// for convenience, define U.  We call it u in LiE because 'U' is not allowed by LiE.
		lie.exec("u = unipRad("+pString+")", ERR);
		
		// compute goesUp( w0^nu0, u), and collect it into an int[][].  do not 
		// display:  we wish to display more precise information using compare.
		lie.exec("u1 = goesUp( w0^nu0, u)", ERR);
		lie.exec("u2 = goesDown(w0^nu0, u)", ERR);
		int[][] uRoots = Algorithms.readLiEMatrix(lie.exec("u1^u2", HIDE));
		int[][] uPrimeRoots = Algorithms.readLiEMatrix(lie.exec("u1", HIDE));
		
		say("Roots in U sent up by w0^nu0-- those sent into the Levi first.");
		//display more precise information using compare.  first what is mapped up and into the levi, L of Q
		lie.exec("compare( w0^nu0, intersection( u, preimage( w0^nu0, levi("+qString+"))))", SHOW);
		// and then what is mapped into the unipotent radical V.
		lie.exec("compare( w0^nu0, intersection( u, preimage( w0^nu0, unipRad("+qString+"))))", SHOW);
		
			
		Unipotent delta  = new Unipotent(deltaRoots, "d"); 
		Unipotent u = new Unipotent(uRoots, "u");
		
		//multiply u by d on the left 
		u = Unipotent.multiply(delta, u);

		
		/* 
		 * Rearrange into the form u' delta u" where 
		 * w0 nu0 maps u' into N and u" into N^-.
		 */
		while(u.indexOf(uPrimeRoots, u.indexOf(deltaRoots))!= -1){
		    
		    //say(u.toString());
		    try{
		    u.pushLeft(u.indexOf(uPrimeRoots, u.indexOf(deltaRoots)));
		    }
		    catch(Exception e){
			System.out.println(u);
			System.out.println("index of delta roots: " +u.indexOf(deltaRoots)+ "\tSubsequent index of uPrimeRoots: "+ u.indexOf(uPrimeRoots, u.indexOf(deltaRoots))+ "\tLength: " + u.getNumberOfTerms());
			System.exit(1);
		    }
		    u.cleanUp();
		    u.clearZeros();
		    psi = u.chOfVars(psi);
		    /*say(u.toString());
		    say(psi.toString());
			System.in.read();*/

		    
		    
		}
		
		

		 System.out.println(u);
		 System.out.println(delta);
		System.out.println(psi);
		//System.in.read();
		
		 // reset psi 
		psi =psi1;
		
		
		
		

	    }
	    

	    
	} catch (IOException e) {
	    System.out.println("Problem starting LiE.");
	    e.printStackTrace();
	    System.exit(1);
	} catch (Exception e) {
	    System.out.println("Exception encountered.  Possible cause:  problem reading LiE output.");

	    e.printStackTrace();
	}



	
    }

    
    private static String toScreen(BufferedReader lieOut) throws IOException, InterruptedException {

	String s1 = lieOut.readLine();
	StringBuffer sb = new StringBuffer();
	while( ! s1.trim().matches("done.")){
	    System.out.println(s1);
	    sb.append(s1);
	    s1 = lieOut.readLine();
	    
	}
	return sb.toString();
	
}

    private static String collect(BufferedReader lieOut) throws IOException {
	String s1 = lieOut.readLine();
	StringBuffer sb = new StringBuffer();
	while(! s1.trim().matches("done.")){
	    sb.append(s1);
	    s1 = lieOut.readLine();
	    
	}
	return sb.toString();
}

private static void lieDo(String string, BufferedWriter lieIn ) throws IOException {
	say(string);

	lieIn.write( string);
	lieIn.newLine();
	
	/*
	 * This next bit is to enable the methods tasked with 
	 * receiving responses from LiE to do their jobs.
	 * 
	 */
	lieIn.write("print(\"done.\")");
	lieIn.newLine();
	
	lieIn.flush();

	
}


    
    
    private static void say(String string) {
	System.out.println(string);
    }

 
    private static String lieVectorString(int[] is) {
	StringBuilder sb = new StringBuilder("[");
	for(int i = 0 ; i <is.length; i++){
	    sb.append(Integer.toString(is[i]));
	    if( i < is.length-1)
		sb.append(",");
	    
	    
	}
	sb.append("]");
	
	return sb.toString();
    }

    private static boolean checkPara(String string, int rank) {
	if(string.startsWith("[")&& string.endsWith("]")){
	    String ss=string.substring(1, string.length()-1);
	    String [] digits = ss.split(",");
	    int n =0;
	    int n1=0;
	    for(String d:digits){
		d=d.trim();
		try{
		n1 = Integer.parseInt(d);
		}
		catch( NumberFormatException e){
		    return false;
		}
		if(n1<1 || n1>rank)
		    return false;
		
		
	    }
	    return true;
	}
	return false;
    }

    private static String type(int rank) {
	switch (rank){
	case 2: return "G";
	case 4: return "F";
	case 6: return "E";
	case 7: return "E";
	case 8: return "E";
	default: return null;
	}
	    
    
    }

}
